package org.yunai.swjg.server.module.quest;

import org.yunai.swjg.server.core.constants.SysMessageConstants;
import org.yunai.swjg.server.core.service.Online;
import org.yunai.swjg.server.module.currency.CurrencyService;
import org.yunai.swjg.server.module.inventory.Inventory;
import org.yunai.swjg.server.module.player.vo.Player;
import org.yunai.swjg.server.module.quest.condition.IQuestCondition;
import org.yunai.swjg.server.module.quest.template.QuestTemplate;
import org.yunai.swjg.server.module.quest.vo.DoingQuest;
import org.yunai.swjg.server.rpc.message.S_C.S_C_FinishQuestResp;
import org.yunai.swjg.server.rpc.message.S_C.S_C_QuestUpdateResp;
import org.yunai.swjg.server.rpc.message.S_C.S_C_SysMessageReq;
import org.yunai.swjg.server.rpc.struct.StQuestCondition;
import org.yunai.swjg.server.rpc.struct.StQuestInfo;
import org.yunai.yfserver.spring.BeanManager;
import org.yunai.yfserver.time.TimeService;
import org.yunai.yfserver.util.Assert;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 任务逻辑基类
 * User: yunai
 * Date: 13-5-8
 * Time: 下午11:20
 */
public abstract class AbstractQuest {

    protected static TimeService timeService;
    protected static CurrencyService currencyService;
    static {
        timeService = BeanManager.getBean(TimeService.class);
        currencyService = BeanManager.getBean(CurrencyService.class);
    }

    /**
     * 任务模版
     */
    private QuestTemplate template;
    /**
     * 前置任务列表
     */
    private List<AbstractQuest> preQuests = new ArrayList<>(1);
    /**
     * 后置任务
     */
    private List<AbstractQuest> nextQuests = new ArrayList<>(1);

    public AbstractQuest(QuestTemplate template) {
        this.template = template;
    }

    public void addPreQuest(AbstractQuest quest) {
        preQuests.add(quest);
    }

    public void addNextQuest(AbstractQuest quest) {
        nextQuests.add(quest);
    }

    public QuestTemplate getTemplate() {
        return template;
    }

    public Integer getId() {
        return getTemplate().getId();
    }

    public List<IQuestCondition> getFinishConditions() {
        return getTemplate().getFinishConditions();
    }

    /**
     * 验证是否能够接受任务<br />
     * <pre>
     *     1. 等级条件是否足够
     *     2. 是否正在进行中
     *     3. 前置任务是否完成
     *     4. 是否满足{@link IQuestCondition}条件
     *     5. 子类实现方法{@link #canAcceptImpl(org.yunai.swjg.server.core.service.Online, boolean)}
     * </pre>
     *
     * @param online    在线信息
     * @param showError 是否提示错误消息给客户端
     * @return 是否能够接受任务
     */
    private boolean canAccept(Online online, boolean showError) {
        Player player = online.getPlayer();
        // 等级是否足够
        if (template.getAcceptMinLevel() > player.getLevel()) {
            if (showError) {
                online.write(new S_C_SysMessageReq(SysMessageConstants.QUEST_ACCEPT_LEVEL_NOT_ENOUGH));
            }
            return false;
        }
        // 是否正在进行中的任务
        QuestDiary questDiary = online.getQuestDiary();
        if (questDiary.getDoingQuestInfo(getId()) != null) {
            if (showError) {
                online.write(new S_C_SysMessageReq(SysMessageConstants.QUEST_ACCEPT_DOING));
            }
            return false;
        }
        // 前置任务是否完成
        for (AbstractQuest preQuest : preQuests) {
            if (questDiary.getFinishedQuestInfo(preQuest.getId()) == null) {
                if (showError) {
                    online.write(new S_C_SysMessageReq(SysMessageConstants.QUEST_ACCEPT_PRE_QUEST_NOT_FINISHED));
                }
                return false;
            }
        }
        // 是否满足接受前置条件(IQuestCondition) TODO
        return true;
    }

    /**
     * 子类实现实现是否能够接受任务
     *
     * @param online    在线信息
     * @param showError 是否提示错误消息给客户端
     * @return 是否能够接受任务
     */
    protected abstract boolean canAcceptImpl(Online online, boolean showError);

    /**
     * 接受任务
     *
     * @param online 在线信息
     */
    public void accept(Online online) {
        // 验证
        if (!canAccept(online, true)) {
            return;
        }
        // 创建任务
        DoingQuest doingQuest = DoingQuest.save(online.getPlayer(), this, timeService.now());
        online.getQuestDiary().addDoingQuestInfo(doingQuest);
        // 发送任务变化
        online.write(new S_C_QuestUpdateResp(genDoingQuestInfo(online, doingQuest)));
        // TODO 记录日志（等日志服务器）
    }

    /**
     * 验证是否能够取消任务<br />
     * <pre>
     *     1. 任务是否是进行中的
     *     2. 子类实现方法{@link #canCancelImpl(org.yunai.swjg.server.core.service.Online, boolean)}
     * </pre>
     *
     * @param online    在线信息
     * @param showError 是否提示错误消息给客户端
     * @return 是否能够取消任务
     */
    private boolean canCancel(Online online, boolean showError) {
        // 是否正在进行
        QuestDiary questDiary = online.getQuestDiary();
        if (questDiary.getDoingQuestInfo(getId()) == null) {
            if (showError) {
                online.write(new S_C_SysMessageReq(SysMessageConstants.QUEST_CANCEL_NOT_DOING));
            }
            return false;
        }
        return canCancelImpl(online, showError);
    }

    /**
     * 子类实现实现是否能够取消任务
     *
     * @param online    在线信息
     * @param showError 是否提示错误消息给客户端
     * @return 是否取消接受任务
     */
    protected abstract boolean canCancelImpl(Online online, boolean showError);

    /**
     * 取消任务
     *
     * @param online 在线信息
     */
    public void cancel(Online online) {
        // 验证
        if (!canCancel(online, true)) {
            return;
        }
        // 取消任务
        DoingQuest doingQuest = online.getQuestDiary().removeDoingQuestInfo(getId());
        doingQuest.submitDelete();
        // 发送任务变化
        StQuestInfo questInfo = genCanAcceptQuestInfo(online);
        if (questInfo != null) {
            online.write(new S_C_QuestUpdateResp(questInfo));
        }
        // TODO 记录日志（等日志服务器）
    }

    /**
     * 验证是否能够完成任务<br />
     * <pre>
     *     1. 是否正在进行中
     *     2. 是否满足{@link IQuestCondition}条件
     *     5. 子类实现方法{@link #canFinishImpl(org.yunai.swjg.server.core.service.Online, boolean)}
     * </pre>
     *
     * @param online    在线信息
     * @param showError 是否提示错误消息给客户端
     * @return 是否能够接受任务
     */
    private boolean canFinish(Online online, boolean showError) {
        // 验证是否正在进行中
        QuestDiary questDiary = online.getQuestDiary();
        if (questDiary.getDoingQuestInfo(getId()) == null) {
            if (showError) {
                online.write(new S_C_SysMessageReq(SysMessageConstants.QUEST_FINISH_NOT_DOING));
            }
            return false;
        }
        // 验证条件是否满足
        for (IQuestCondition condition : getFinishConditions()) {
            if (!condition.isMeet(online, getId(), showError)) {
                return false;
            }
        }
        return canFinishImpl(online, showError);
    }

    protected abstract boolean canFinishImpl(Online online, boolean showError);

    /**
     * 完成任务
     *
     * @param online 在线信息
     */
    public void finish(Online online) {
        // 验证
        if (!canFinish(online, true)) {
            return;
        }
        // 检查背包
        Inventory inventory = online.getInventory();
        if (!inventory.checkPrimBagSpace(template.getBonusItems(), true)) {
            return;
        }
        // 检查金钱是否可以给
        if (!currencyService.canAdd(online, template.getBonusCurrency(), template.getBonusCurrencyNum(), true)) {
            return;
        }
        // 执行finishCondition的onFinish
        for (IQuestCondition finishCondition : getFinishConditions()) {
            finishCondition.onFinish(online);
        }
        // 奖励金钱
        currencyService.add(online, template.getBonusCurrency(), template.getBonusCurrencyNum());
        // 奖励经验
        online.getPlayer().addExp(template.getBonusExp());
        // 奖励道具
        inventory.addPrimBagItems(template.getBonusItems());
        // 完成任务
        QuestDiary questDiary = online.getQuestDiary();
        DoingQuest doingQuest = questDiary.removeDoingQuestInfo(getId());
        doingQuest.submitDelete();
        finishImpl(online, doingQuest);
        // 发送任务完成
        online.write(new S_C_FinishQuestResp(getId()));
        // 重新发送任务列表
        questDiary.sendQuestList();

        // TODO 记录日志（等待日志服务器）
    }

    /**
     * 子类实现，进一步做处理
     *
     * @param online 在线信息
     * @param doingQuest 进行中的任务信息
     */
    protected abstract void finishImpl(Online online, DoingQuest doingQuest);

    /**
     * 生成正在进行中的任务的任务详细信息
     *
     * @param online     在线信息
     * @param doingQuest 正在进行中的任务
     * @return 任务详细信息
     */
    public StQuestInfo genDoingQuestInfo(Online online, DoingQuest doingQuest) {
        Assert.isTrue(doingQuest.getQuest() == this);
        StQuestInfo questInfo = new StQuestInfo();
        questInfo.setId(getId());
        if (canFinish(online, false)) {
            questInfo.setStatus(QuestDef.Status.CAN_FINISHED.getValue());
        } else {
            questInfo.setStatus(QuestDef.Status.ACCEPTED.getValue());
        }
        questInfo.setFinishConditions(genFinishConditions(online));
        return questInfo;
    }

    /**
     * 生成可以接受的任务的任务详细信息
     *
     * @param online 在线信息
     * @return 任务详细信息
     */
    public StQuestInfo genCanAcceptQuestInfo(Online online) {
        if (!canAccept(online, false)) {
            return null;
        }
        StQuestInfo questInfo = new StQuestInfo();
        questInfo.setId(getId());
        questInfo.setStatus(QuestDef.Status.CAN_ACCEPTED.getValue());
        questInfo.setFinishConditions(genFinishConditions(online));
        return questInfo;
    }

    @SuppressWarnings("unchecked")
    public final List<StQuestCondition> genFinishConditions(Online online) {
        AbstractQuest quest = online.getQuestDiary().getDoingQuest(getId());
        if (quest == null) {
            return Collections.EMPTY_LIST;
        }
        List<StQuestCondition> finishConditions = new ArrayList<>(quest.getFinishConditions().size());
        for (IQuestCondition cond : quest.getFinishConditions()) {
            finishConditions.add(cond.genFinishCondition(online, getId()));
        }
        return finishConditions;
    }

    /**
     * 创建任务对象简单工厂
     */
    public static class Factory {

        /**
         * 创建任务对象
         *
         * @param template 模版
         * @return 任务对象
         */
        public static AbstractQuest create(QuestTemplate template) {
            switch (template.getType()) {
                case MAIN:
                case BRANCH:
                    return new CommonQuest(template);
                default:
                    throw new RuntimeException("暂时支持该任务类型");
            }
        }
    }


}
